#coding=utf-8
from __future__ import with_statement
#   Author:Zhangbo
#   Email:zhangboks@gmail.com  
#   QQ(If you have):513364476

import collections
import errno
import logging
import socket
import sys
import os

try:
    from netMinc import reactor
except ImportError:
    import init_netminc

class Buffer(object):
    def __init__(self, sock, _reactor = None):
        self.socket             = sock
        self.socket.setblocking(0)
        self._read_buffer       = collections.deque()
        self._write_buffer      = collections.deque()
        self._reactor           = _reactor or reactor.Reactor.instance()
        self._close_callback    = None
        self._max_buffer_size   = 1024*1024*100
        self._read_bytes        = 0
        self._read_until        = False
        self._read_spliter      = ""
        self._is_connecting     = False
        self._state             = self._reactor.ERROREV
        self._write_callback    = None
        self._read_callback     = None
        self._read_buffer_size  = 0 
        self._write_pause       = False
        self._buffer_done       = False
        self._time_out_ev       = None
        self._init_buffer_reactor()

    def _init_buffer_reactor(self):
        self._reactor.add_handler(self.socket.fileno(), self._event_handler, self._state)

    def _set_buffer_event(self, event):
        if not self.socket:
            return
        if not self._state & event:
            self._state = self._state | event
            self._reactor.mod_handler(self.socket.fileno(), self._state)   

    def _mov_buffer_event(self, event):
        if not self.socket:
            return 
        if  self._state & event:
            self._state &= (~event)
            self._reactor.mod_handler(self.socket.fileno(), self._state)

    def read_bytes(self, num, callback):
        if not num or not callback:
            return 
        self._read_bytes = num
        self._read_callback = callback
        self._set_buffer_event(self._reactor.READEV)

    def read_until(self, spliter, callback):
        if not spliter or not callback:
            return
        self._read_until = True
        self._read_spliter = spliter
        self._read_callback = callback
        self._set_buffer_event(self._reactor.READEV)

    def write(self, data, callback = None):
        if not self.socket:
            return
        if data:
            self._write_buffer.append(data)
        self._write_callback = callback
        # self._set_buffer_event(self._reactor.WRITEEV)

    #interface of reactor's callback
    def _event_handler(self, fd, event):
        if not self.socket:
            logging.warning("socket closed %d", fd)
            return
        try:
            if event & self._reactor.READEV:
                self._handle_readev()
            if event & self._reactor.ERROREV:
                self.close()
            if event & self._reactor.WRITEEV:
                if self._is_connecting:
                    self._handle_connect()
                self._write_pause = False
            self._handle_writeev()
            return 

        except:
            logging.error("event handler error", exc_info = True)
            self.close()
            raise

    def close(self):
        if self.socket:
            self._reactor.mov_handler(self.socket.fileno())
            self.socket.close()
            self.socket = None
            if self._close_callback:
                self._run_callback(self._close_callback)

    def _run_callback(self, callback):
        try:
            callback()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self._handle_callback_exception(callback)

    def _handle_callback_exception(self, callback):
        logging.error("Exception when call %r", callback, exc_info = True)

    def connect(self, address, callback = None):
        self._is_connecting = True
        try:
            self.socket.connect(address)
        except socket.error, e:
            if e.args[0] in (errno.EINPROGRESS, errno.EWOULDBLOCK):
                raise
        self._handle_connect = callback

    def add_close_handler(self, handler):
        if handler:
            self._close_callback = handler

    def _handle_readev(self):
        self._mov_data_from_socket_to_buffer()
        self._mov_data_from_buffer_to_user()
            
    def _handle_writeev(self):
        self._cut_write_buffer(262144)
        while 1:
            try:
                if not self.socket:
                    return
                if self._write_pause:
                    break
                n = 0
                if self._write_buffer and self._write_buffer[0]:
                    n = self.socket.send(self._write_buffer[0])
                else:
                    self._buffer_done = True
                if n:
                    self._cut_write_buffer(n)
                    self._write_buffer.popleft()
            except socket.error, e:
                if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                    self._write_pause = True
                    self._set_buffer_event(self._reactor.WRITEEV)
                    break
                else:
                    logging.warning("Write error at _handle_writeev")
            if self._buffer_done:
                self._mov_buffer_event(self._reactor.WRITEEV)
                if not self._write_buffer or not self._write_buffer[0]:
                    if self._write_callback:
                        self._write_callback()
                break
                    
    def _mov_data_from_socket_to_buffer(self):
        while 1:
            try:
                data, err = self._read_from_socket()
            except socket.error, e:
                if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN, errno.EPIPE):
                    if e.args[0] == errno.EPIPE:
                        self.close()
                else:
                    logging.warning("Read data from socket error")
                break

            if not data:
                if err in (errno.EWOULDBLOCK, errno.EAGAIN, errno.EPIPE):
                    return True
                self.close()
                return False
            self._read_buffer.append(data)
            self._read_buffer_size += len(data)
            if self._read_buffer_size > self._max_buffer_size:
                logging.error("Read buffer full")
                return False
        return True

    def _read_from_socket(self):
        try:
            data = self.socket.recv(4096)
        except socket.error, e:
            if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                return None, e.args[0]
            else:
                raise
        except (OSError, IOError), e:
            if e.args[0] == errno.EPIPE:
                self.socket.close()
                return data, e.args[0]
        return data, None

    def _mov_data_from_buffer_to_user(self):
        if self._read_bytes:
            if self._read_bytes <= self._read_buffer_size:
                data = self._get_datas_by_size(self._read_bytes)
                if data:
                    self._read_callback(data)
                else:
                    return None
        elif self._read_until:
            self._cut_read_buffer(self._max_buffer_size)
            if not self._read_buffer[0]:
                return None
            #self._read_buffer[0] is instance of str but I don't know
            #why this raise an exception, If you know, please call me
            location = str(self._read_buffer[0]).find(self._read_spliter)
            if location == -1:
                return None
            data = self._get_datas_by_size(location + len(self._read_spliter))
            if data:
                self._read_callback(data)
        return None

    def _get_datas_by_size(self, size):
        if self._cut_read_buffer(size):
            ret = self._read_buffer.popleft()
            if ret:
                self._read_buffer_size -= len(ret)
                return ret
            return None
        return None

    def _cut_read_buffer(self, size):
        remaining = size
        head_buffer = []
        while self._read_buffer and remaining > 0:
            chunk = self._read_buffer.popleft()
            if not chunk:
                return False
            if len(chunk) > remaining:
                self._read_buffer.appendleft(chunk[remaining:])
                chunk = chunk[:remaining]            
            head_buffer.append(chunk)
            remaining -= len(chunk)
        self._read_buffer.appendleft(''.join(head_buffer))

        return True

    def _cut_write_buffer(self, size):
        remaining = size
        head_buffer = []
        while self._write_buffer and remaining > 0:
            chunk = self._write_buffer.popleft()
            if not chunk or chunk == '':
                return False
            if len(chunk) > remaining:
                self._write_buffer.appendleft(chunk[remaining:])
                chunk = chunk[:remaining]            
            head_buffer.append(chunk)
            remaining -= len(chunk)
        self._write_buffer.appendleft(''.join(head_buffer))

        return True        

    def add_timeout(self, deadline, callback):
        self._time_out_ev = self._reactor.add_timeout_ev(deadline, callback)

    def mov_timeout(self):
        if self._time_out_ev:
            return self._reactor.mov_timeout_ev(self._time_out_ev)

    def close(self):
        if self.socket:
            self._reactor.mov_handler(self.socket.fileno())
            self.socket.close()
            self.socket = None
        if self._close_callback:
            self._close_callback()

if __name__ == "__main__":
    import socket
    import functools

    class HTTPServer(object):
        def __init__(self, _buffer, request_callback):
            self._buffer = _buffer
            self.request_callback = request_callback
            self._read_data()

        def _read_data(self):
            self._buffer.read_until("\r\n\r\n", self._on_read)

        def _on_read(self, data):
            print data
            self._buffer.write(self.request_callback(data))

    def on_request(data):
        ret =  data
        print ret
        return ret

    def connection_ready(sock, fd, events):
        while 1:
            try:
                con, add = sock.accept()
            except socket.error, e:
                if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                    return
            if con:
                con.setblocking(0)
                print  con, add
                _Buf = Buffer(con)
                HTTPServer(_Buf, on_request)
 

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setblocking(0)
    sock.bind(("", 1115))
    sock.listen(512)

    _reactor = reactor.Reactor.instance()
    _reactor.add_handler(sock.fileno(), functools.partial(connection_ready, sock), _reactor.READEV)
    _reactor.run()
